// ----------------------------------------------------------------------------
// seed_seq.h
// ----------------------------------------------------------------------------
/*
 Description:
 乱数のseedを準備するためのコンテナ. std::vectorを内部で所有しないようにする. 
 std::seed_array と互換性を保つためにライブラリの命名規則と異なる表記を行う.
 
 ----------------------------------------------------------------
 使い方.
 
 ・std::seed_arrayの代わりに利用する.
 
    fw ::seed_array<8>  seed = { 1, 2, 3, 4, 5, 6, 7, 8, };
    std::mt19937 engine(seed);
    std::uniform_real_distribution<float> distribution(0.0f, 1.0f);
 
    ...
 
    float randomValueF = distribution(engine); // 適当な乱数値.
 
 */
#pragma once
#include <fw/base/core/noncopyable.h>
#include <fw/base/core/stdinteger.h>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <type_traits>
#include <algorithm>
#include <initializer_list>

namespace fw
{
    template<std::size_t N> struct seed_array
    {
    public:
        static_assert(N>0, "N>0");
        
        typedef std::uint_least32_t result_type;
        
        seed_array()
        : v()
        {
        }
        
        template<class T>       seed_array(std::initializer_list<T> il)
        {
            init_(il.begin(), il.end());
        }
 
        template<class Iter>    seed_array(Iter first, Iter last)
        {
            init_(first, last);
        }
        
        static std::size_t size() { return N; }
        template<class Iterator> void param(Iterator dest) const
        {
            std::copy(&v[0], &v[N], dest);
        }
        
        template<class Iter> void generate(Iter first, Iter last) const
        {
            typedef typename std::iterator_traits<Iter>::value_type value_type;
            static_assert(
                          std::is_unsigned<value_type>::value && sizeof(result_type) <= sizeof(value_type),
                          "value_type shall denote an unsigned integer type"
                          "capable of accommodating 32-bit quantities."
            );
            
            if(first==last) { return; }
            
            const std::size_t n = static_cast<std::size_t>(last - first);
            const std::size_t s = N;
            //const value_type  mask = result_type(0xffffffffu);
            const auto T = [](value_type x) { return x ^ (x >> 27); };
            
            // 初期化.
            std::fill(first, last, static_cast<value_type>(0x8b8b8b8bu));
            
            // パラメータ設定.
            const std::size_t t = (n >= 623) ? 11 :
                                  (n >=  68) ?  7 :
                                  (n >=  39) ?  5 :
                                  (n >=   7) ?  3 :
                                  (n - 1) / 2;
            const std::size_t p = (n - t) / 2;
            const std::size_t q = p + t;
            const std::size_t m = (std::max)(s+1, n);
            
            // 標準ライブラリ関数のrandomより引用. 
            // k = 0;
            {
                result_type r  = 1664525 * T(first[0] ^ first[p] ^ first[n-1]);
                first[p] += r;
                r        += s;
                first[q] += r;
                first[0]  = r;
            }
            for(std::size_t k=1; k<=s; ++k)
            {
                const std::size_t k_mod_n   = (k % n);
                const std::size_t k_plus_p  = (k + p) % n;
                result_type r = 1664525 * T(first[k_mod_n] ^ first[k_plus_p] ^ first[(k-1)%n]);
                first[k_plus_p] += r;
                r               += k_mod_n + v[k-1];
                first[(k+q)%n]  += r;
                first[k_mod_n]   = r;
            }
            for(std::size_t k=s+1; k<m; ++k)
            {
                const std::size_t k_mod_n   = (k % n);
                const std::size_t k_plus_p  = (k + p) % n;
                result_type r = 1664525 * T(first[k_mod_n] ^ first[k_plus_p] ^ first[(k-1)%n]);
                first[k_plus_p] += r;
                r               += k_mod_n;
                first[(k+q)%n]  += r;
                first[k_mod_n]   = r;
            }
            for(std::size_t k=m; k<m+n; ++k)
            {
                const std::size_t k_mod_n   = (k % n);
                const std::size_t k_plus_p  = (k + p) % n;
                result_type r = 1566083941 * T(first[k_mod_n] + first[k_plus_p] + first[(k-1)%n]);
                first[k_plus_p] ^= r;
                r               -= k_mod_n;
                first[(k+q)%n]  ^= r;
                first[k_mod_n]   = r;
                
            }
        }
        
    private:
        
        result_type     v[N];
        
        template<class Iter> void init_(Iter first, Iter last)
        {
            for(std::size_t i=0; i<N; ++i, ++first)
            {
                if(first==last)
                {
                    for(; i<N; ++i)
                    {
                        v[i] = 0;
                    }
                    return;
                }
                v[i] = *first;
            }
            
            for(std::size_t i=0; first!=last; ++first)
            {
                v[i] ^= *first;
                if(++i >= N) { i -= N; }
            }
        }
    
    }; // struct seed_array
    
    
} // namespace fw
